package com.cw.lang.mybatis.dto;

import java.io.Serializable;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.github.pagehelper.Page;

import lombok.Data;

/**
 * 对Page结果进行包装
 * <p/>
 * 新增分页的多项属性，主要参考:http://bbs.csdn.net/topics/360010907
 *
 * @author liuzh/abel533/isea533
 * @version 3.3.0
 * @since 3.2.2 项目地址 : http://git.oschina.net/free/Mybatis_PageHelper
 */
@Data
public class PageInfo<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    /** 当前页 */
    private int pageNum;
    /** 每页的数量 */
    private int pageSize;
    /** 当前页的数量 */
    private int size;

    /** 当前页面第一个元素在数据库中的行号 */
    private long startRow;
    /** 当前页面最后一个元素在数据库中的行号 */
    private long endRow;
    /** 总记录数 */
    private long total;
    /** 总页数 */
    private int pages;
    /** 结果集 */
    private List<T> list;

    /** 前一页 */
    private int prePage;
    /** 下一页 */
    private int nextPage;

    /** 是否为第一页 */
    private boolean isFirstPage = false;
    /** 是否为最后一页 */
    private boolean isLastPage = false;
    /** 是否有前一页 */
    private boolean hasPreviousPage = false;
    /** 是否有下一页 */
    private boolean hasNextPage = false;
    /** 导航页码数 */
    private int navigatePages;
    /** 所有导航页号 */
    private int[] navigatePageNums;
    /** 导航条上的第一页 */
    private int navigateFirstPage;
    /** 导航条上的最后一页 */
    private int navigateLastPage;
    /** 分页信息 */
    private Pagination pagination;
    public PageInfo() {}
    @Data
    class Pagination implements Serializable {
        private static final long serialVersionUID = 4142434764488046110L;
        private int current;
        private int total;
        private int pageSize;

        public Pagination(int current,int total,int pageSize){
            this.current = current;
            this.pageSize = pageSize;
            this.total = total;
        }
    }
    /**
     * 包装Page对象
     *
     * @param list 集合
     */
    public PageInfo(List<T> list) {
        this(list, 8);
    }

    /**
     * 对{@link PageInfo#list}进行转换，直接替换为入参list
     *
     * @param pageInfo 原pageInfo，用于提取除list以外的所有信息
     * @param list 新list
     * @param <F> 泛型
     */
    public <F> PageInfo(PageInfo<F> pageInfo, List<T> list) {
        setPageInfoFields(pageInfo);
        this.setList(list);
    }

    /**
     * 返回一个空List的PageInfo
     *
     * @param pageInfo 原pageInfo
     * @param <F> 泛型
     */
    public <F> PageInfo(PageInfo<F> pageInfo) {
        setPageInfoFields(pageInfo);
    }

    /**
     * 对{@link PageInfo#list}进行转换，转换方式使用function
     *
     * @param pageInfo 原pageInfo，用于提取除list以外的所有信息
     * @param function 转换逻辑
     */
    public <F> PageInfo(PageInfo<F> pageInfo, Function<? super F, ? extends T> function) {
        setPageInfoFields(pageInfo);
        List<F> list = pageInfo.getList();
        if (list != null && !list.isEmpty()) {
            this.setList(list.stream()
                .map(function)
                .collect(Collectors.toList()));
        }
    }

    /**
     * 设置除list以外的所有信息
     *
     * @param pageInfo 原pageInfo
     * @param <F> 泛型
     */
    private <F> void setPageInfoFields(PageInfo<F> pageInfo) {
        this.setPageNum(pageInfo.getPageNum());
        this.setPageSize(pageInfo.getPageSize());
        this.setPages(pageInfo.getPages());
        this.setSize(pageInfo.getSize());
        this.setTotal(pageInfo.getTotal());
        this.setStartRow(pageInfo.getStartRow());
        this.setEndRow(pageInfo.getEndRow());
        this.setPrePage(pageInfo.getPrePage());
        this.setNextPage(pageInfo.getNextPage());
        this.setFirstPage(pageInfo.isFirstPage());
        this.setLastPage(pageInfo.isLastPage());
        this.setHasPreviousPage(pageInfo.isHasPreviousPage());
        this.setHasNextPage(pageInfo.isHasNextPage());
        this.setNavigatePages(pageInfo.getNavigatePages());
        this.setNavigatePageNums(pageInfo.getNavigatePageNums());
        this.setNavigateFirstPage(pageInfo.getNavigateFirstPage());
        this.setNavigateLastPage(pageInfo.getNavigateLastPage());
        this.setPagination(new Pagination(pageInfo.getPageNum(),(int)pageInfo.getTotal(),pageInfo.getPageSize()));
    }

    /**
     * 包装Page对象
     *
     * @param list page结果
     * @param navigatePages 页码数量
     */
    private PageInfo(List<T> list, int navigatePages) {
        if (list instanceof Page) {
            Page<T> page = (Page<T>) list;
            this.pageNum = page.getPageNum();
            this.pageSize = page.getPageSize();

            this.pages = page.getPages();
            this.list = page;
            this.size = page.size();
            this.total = page.getTotal();
            // 由于结果是>startRow的，所以实际的需要+1
            if (this.size == 0) {
                this.startRow = 0L;
                this.endRow = 0L;
            } else {
                this.startRow = page.getStartRow() + 1L;
                // 计算实际的endRow（最后一页的时候特殊）
                this.endRow = this.startRow - 1 + this.size;
            }
            this.pagination = new Pagination(page.getPageNum(),(int)page.getTotal(),page.getPageSize());
        } else if (list != null) {
            this.pageNum = 1;
            this.pageSize = list.size();

            this.pages = this.pageSize > 0 ? 1 : 0;
            this.list = list;
            this.size = list.size();
            this.total = list.size();
            this.startRow = 0L;
            this.endRow = !list.isEmpty() ? list.size() - 1 : 0L;
        }
        if (list != null) {
            this.navigatePages = navigatePages;
            // 计算导航页
            calcNavigatePageNums();
            // 计算前后页，第一页，最后一页
            calcPage();
            // 判断页面边界
            judgePageBoundary();
        }
    }

    /**
     * 计算导航页
     */
    private void calcNavigatePageNums() {
        // 当总页数小于或等于导航页码数时
        if (pages <= navigatePages) {
            navigatePageNums = new int[pages];
            for (int i = 0; i < pages; i++) {
                navigatePageNums[i] = i + 1;
            }
        }
        // 当总页数大于导航页码数时
        else {
            navigatePageNums = new int[navigatePages];
            int startNum = pageNum - navigatePages / 2;
            int endNum = pageNum + navigatePages / 2;

            if (startNum < 1) {
                startNum = 1;
                // (最前navigatePages页
                for (int i = 0; i < navigatePages; i++) {
                    navigatePageNums[i] = startNum++;
                }
            } else if (endNum > pages) {
                endNum = pages;
                // 最后navigatePages页
                for (int i = navigatePages - 1; i >= 0; i--) {
                    navigatePageNums[i] = endNum--;
                }
            } else {
                // 所有中间页
                for (int i = 0; i < navigatePages; i++) {
                    navigatePageNums[i] = startNum++;
                }
            }
        }
    }

    /**
     * 计算前后页，第一页，最后一页
     */
    private void calcPage() {
        if (navigatePageNums != null && navigatePageNums.length > 0) {
            navigateFirstPage = navigatePageNums[0];
            navigateLastPage = navigatePageNums[navigatePageNums.length - 1];
            if (pageNum > 1) {
                prePage = pageNum - 1;
            }
            if (pageNum < pages) {
                nextPage = pageNum + 1;
            }
        }
    }

    /**
     * 判定页面边界
     */
    private void judgePageBoundary() {
        isFirstPage = pageNum == 1;
        isLastPage = pageNum == pages || pages == 0;
        hasPreviousPage = pageNum > 1;
        hasNextPage = pageNum < pages;
    }

    public static final class Builder<T> {

        private PageInfo<T> instance = new PageInfo<>();

        private Builder() {}

        /**
         * 获取实例
         *
         * @param <T> 泛型
         * @return {@link Builder}
         */
        public static <T> Builder<T> getInstance() {
            return new Builder<>();
        }

        /**
         * 获取实例
         *
         * @param <T> 泛型
         * @param instance 原实例
         * @return {@link Builder}
         */
        public static <T> Builder<T> getInstance(PageInfo<T> instance) {
            Builder<T> builder = new Builder<>();
            builder.instance = instance;
            return builder;
        }

        /**
         * 添加参数
         *
         * @param pageNum 参数
         * @return {@link Builder}
         */
        public Builder<T> addPageNum(int pageNum) {
            this.instance.setPageNum(pageNum);
            return this;
        }

        /**
         * 添加参数
         *
         * @param pageSize 参数
         * @return {@link Builder}
         */
        public Builder<T> addPageSize(int pageSize) {
            this.instance.setPageSize(pageSize);
            return this;
        }

        /**
         * 添加参数
         *
         * @param size 参数
         * @return {@link Builder}
         */
        public Builder<T> addSize(int size) {
            this.instance.setSize(size);
            return this;
        }

        /**
         * 添加参数
         *
         * @param startRow 参数
         * @return {@link Builder}
         */
        public Builder<T> addStartRow(long startRow) {
            this.instance.setStartRow(startRow);
            return this;
        }

        /**
         * 添加参数
         *
         * @param endRow 参数
         * @return {@link Builder}
         */
        public Builder<T> addEndRow(long endRow) {
            this.instance.setEndRow(endRow);
            return this;
        }

        /**
         * 添加参数
         *
         * @param total 参数
         * @return {@link Builder}
         */
        public Builder<T> addTotal(long total) {
            this.instance.setTotal(total);
            return this;
        }

        /**
         * 添加参数
         *
         * @param pages 参数
         * @return {@link Builder}
         */
        public Builder<T> addPages(int pages) {
            this.instance.setPages(pages);
            return this;
        }

        /**
         * 添加参数
         *
         * @param list 参数
         * @return {@link Builder}
         */
        public Builder<T> addList(List<T> list) {
            this.instance.setList(list);
            return this;
        }

        /**
         * 添加参数
         *
         * @param prePage 参数
         * @return {@link Builder}
         */
        public Builder<T> addPrePage(int prePage) {
            this.instance.setPrePage(prePage);
            return this;
        }

        /**
         * 添加参数
         *
         * @param nextPage 参数
         * @return {@link Builder}
         */
        public Builder<T> addNextPage(int nextPage) {
            this.instance.setNextPage(nextPage);
            return this;
        }

        /**
         * 添加参数
         *
         * @param isFirstPage 参数
         * @return {@link Builder}
         */
        public Builder<T> addFirstPage(boolean isFirstPage) {
            this.instance.setFirstPage(isFirstPage);
            return this;
        }

        /**
         * 添加参数
         *
         * @param isLastPage 参数
         * @return {@link Builder}
         */
        public Builder<T> addLastPage(boolean isLastPage) {
            this.instance.setLastPage(isLastPage);
            return this;
        }

        /**
         * 添加参数
         *
         * @param hasPreviousPage 参数
         * @return {@link Builder}
         */
        public Builder<T> addHasPreviousPage(boolean hasPreviousPage) {
            this.instance.setHasPreviousPage(hasPreviousPage);
            return this;
        }

        /**
         * 添加参数
         *
         * @param hasNextPage 参数
         * @return {@link Builder}
         */
        public Builder<T> addHasNextPage(boolean hasNextPage) {
            this.instance.setHasNextPage(hasNextPage);
            return this;
        }

        /**
         * 添加参数
         *
         * @param navigatePages 参数
         * @return {@link Builder}
         */
        public Builder<T> addNavigatePages(int navigatePages) {
            this.instance.setNavigatePages(navigatePages);
            return this;
        }

        /**
         * 添加参数
         *
         * @param navigatePageNums 参数
         * @return {@link Builder}
         */
        public Builder<T> addNavigatePageNums(int[] navigatePageNums) {
            this.instance.setNavigatePageNums(navigatePageNums);
            return this;
        }

        /**
         * 添加参数
         *
         * @param navigateFirstPage 参数
         * @return {@link Builder}
         */
        public Builder<T> addNavigateFirstPage(int navigateFirstPage) {
            this.instance.setNavigateFirstPage(navigateFirstPage);
            return this;
        }

        /**
         * 添加参数
         *
         * @param navigateLastPage 参数
         * @return {@link Builder}
         */
        public Builder<T> addNavigateLastPage(int navigateLastPage) {
            this.instance.setNavigateLastPage(navigateLastPage);
            return this;
        }

        /**
         * 生成实例
         *
         * @return {@link PageInfo}
         */
        public PageInfo<T> build() {
            return this.instance;
        }

    }

    public int getPageNum() {
        return pageNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public long getStartRow() {
        return startRow;
    }

    public void setStartRow(long startRow) {
        this.startRow = startRow;
    }

    public long getEndRow() {
        return endRow;
    }

    public void setEndRow(long endRow) {
        this.endRow = endRow;
    }

    public long getTotal() {
        return total;
    }

    public void setTotal(long total) {
        this.total = total;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    public List<T> getList() {
        return list;
    }

    public void setList(List<T> list) {
        this.list = list;
    }

    public int getPrePage() {
        return prePage;
    }

    public void setPrePage(int prePage) {
        this.prePage = prePage;
    }

    public int getNextPage() {
        return nextPage;
    }

    public void setNextPage(int nextPage) {
        this.nextPage = nextPage;
    }

    public boolean isFirstPage() {
        return isFirstPage;
    }

    public void setFirstPage(boolean firstPage) {
        isFirstPage = firstPage;
    }

    public boolean isLastPage() {
        return isLastPage;
    }

    public void setLastPage(boolean lastPage) {
        isLastPage = lastPage;
    }

    public boolean isHasPreviousPage() {
        return hasPreviousPage;
    }

    public void setHasPreviousPage(boolean hasPreviousPage) {
        this.hasPreviousPage = hasPreviousPage;
    }

    public boolean isHasNextPage() {
        return hasNextPage;
    }

    public void setHasNextPage(boolean hasNextPage) {
        this.hasNextPage = hasNextPage;
    }

    public int getNavigatePages() {
        return navigatePages;
    }

    public void setNavigatePages(int navigatePages) {
        this.navigatePages = navigatePages;
    }

    public int[] getNavigatePageNums() {
        return navigatePageNums;
    }

    public void setNavigatePageNums(int[] navigatePageNums) {
        this.navigatePageNums = navigatePageNums;
    }

    public int getNavigateFirstPage() {
        return navigateFirstPage;
    }

    public void setNavigateFirstPage(int navigateFirstPage) {
        this.navigateFirstPage = navigateFirstPage;
    }

    public int getNavigateLastPage() {
        return navigateLastPage;
    }

    public void setNavigateLastPage(int navigateLastPage) {
        this.navigateLastPage = navigateLastPage;
    }

    public Pagination getPagination() {
        return pagination;
    }

    public void setPagination(Pagination pagination) {
        this.pagination = pagination;
    }
}
